'''
工具类
'''
import re
import random
import string
import time
from typing import List, Set, Optional
from enum import Enum
from dataclasses import dataclass

import pykakasi
import unicodedata


def detect_card_type(card_number):
    if card_number is None or len(card_number) < 6:
        return "unknown"

    if card_number.startswith('4'):
        return "visa"

    prefix6 = int(card_number[:6])
    prefix4 = int(card_number[:4])
    prefix2 = int(card_number[:2])

    if (51 <= prefix2 <= 55) or (2221 <= prefix4 <= 2720):
        return "master"

    if prefix2 == 34 or prefix2 == 37:
        return "amex"

    if 3528 <= prefix4 <= 3589:
        return "jcb"

    if prefix2 == 36 or prefix2 == 38 or prefix2 == 39:
        return "diners"

    return "unknown"


class DomainMode(Enum):
    CUSTOM = "CUSTOM"  # 使用你自己的域名（推荐做 catch-all）
    JP_PUBLIC = "JP_PUBLIC"  # 从常见日本公共邮箱域中挑选


@dataclass
class Options:
    domain_mode: DomainMode = DomainMode.JP_PUBLIC
    custom_domain: str = "example.jp"  # 你自己的域名（支持子域，如 mail.example.jp）
    use_plus_tag: bool = False  # 是否使用 +tag
    ensure_uniqueness: bool = True  # 是否附加随机/时间戳确保唯一
    min_len: int = 8  # local-part 最小长度
    max_len: int = 14  # local-part 最大长度
    base_name: Optional[str] = None  # 可选：基于名字（如 "taro.yamada"）
    use_timestamp_tag: bool = False  # +tag 用时间戳而不是随机


class EmailGenerator:
    # 简单合法性校验正则
    VALID_LOCAL = re.compile(r'^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$')

    # 日本常见公共邮箱域（注意：公共邮箱易触发短信/风控校验，生产建议用自有域名）
    JP_PUBLIC_DOMAINS = [
        "yahoo.co.jp", "gmail.com", "icloud.com", "outlook.com", "hotmail.com", "live.jp",
        "ezweb.ne.jp", "docomo.ne.jp", "softbank.ne.jp", "mineo.jp", "rakuten.jp"
    ]

    # 一次性邮箱（黑名单，非完整列表，示意）
    DISPOSABLE_DOMAINS = {
        "mailinator.com", "tempmail.net", "10minutemail.com", "guerrillamail.com",
        "sharklasers.com", "yopmail.com", "trashmail.com", "mintemail.com"
    }

    LOCAL_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789._-"

    @staticmethod
    def generate(opt: Optional[Options] = None) -> str:
        if opt is None:
            opt = Options()

        domain = EmailGenerator._select_domain(opt)
        if opt.base_name and opt.base_name.strip():
            local = EmailGenerator._sanitize_base(opt.base_name)
        else:
            local = EmailGenerator._random_local(opt.min_len, opt.max_len)

        if opt.ensure_uniqueness:
            local = EmailGenerator._add_uniq_suffix(local)

        if opt.use_plus_tag:
            tag = str(int(time.time())) if opt.use_timestamp_tag else EmailGenerator._random_tag(6)
            local = local + "+" + tag

        # 最终兜底校验（避免非法或过长）
        local = EmailGenerator._fix_local(local, opt)

        return local + "@" + domain

    # —— 辅助方法们 ——

    @staticmethod
    def _select_domain(opt: Options) -> str:
        if opt.domain_mode == DomainMode.CUSTOM:
            domain = opt.custom_domain.lower() if opt.custom_domain else "example.jp"
        else:
            domain = random.choice(EmailGenerator.JP_PUBLIC_DOMAINS)

        # 黑名单兜底
        if domain in EmailGenerator.DISPOSABLE_DOMAINS:
            domain = "example.jp"

        return domain

    @staticmethod
    def _sanitize_base(base: str) -> str:
        # 只保留 [a-z0-9._-]，并做简单规范化
        base = base.lower()
        base = re.sub(r'[^a-z0-9._-]', '.', base)
        base = re.sub(r'\.{2,}', '.', base)  # 连续点压缩
        base = re.sub(r'^[._-]+|[._-]+$', '', base)  # 去首尾符号

        if not base.strip():
            base = EmailGenerator._random_local(8, 12)

        return base

    @staticmethod
    def _random_local(min_len: int, max_len: int) -> str:
        length = random.randint(min_len, max_len)
        chars = []

        # 首字符不得为 ._-
        first_char = random.choice("abcdefghijklmnopqrstuvwxyz0123456789")
        chars.append(first_char)
        prev_char = first_char

        for i in range(1, length - 1):
            char = random.choice(EmailGenerator.LOCAL_CHARS)
            # 避免连续两个 '.'
            if char == '.' and prev_char == '.':
                continue
            chars.append(char)
            prev_char = char

        # 尾字符不得为 ._-
        last_char = random.choice("abcdefghijklmnopqrstuvwxyz0123456789")
        chars.append(last_char)

        result = ''.join(chars)

        # 确保长度在范围内
        if len(result) < min_len:
            result += EmailGenerator._random_tag(min_len - len(result))
        elif len(result) > max_len:
            result = result[:max_len]

        return result

    @staticmethod
    def _add_uniq_suffix(local: str) -> str:
        # 在末尾追加 _ + 6位随机（保持短且足够离散）
        rnd = EmailGenerator._random_tag(6)
        candidate = local + "_" + rnd

        if len(candidate) <= 64:
            return candidate

        # 超长则截断前缀
        keep = max(1, 64 - (1 + len(rnd)))  # "_" + rnd
        return local[:keep] + "_" + rnd

    @staticmethod
    def _random_tag(length: int) -> str:
        chars = string.ascii_lowercase + string.digits
        return ''.join(random.choice(chars) for _ in range(length))

    @staticmethod
    def _fix_local(local: str, opt: Options) -> str:
        # 长度兜底
        if len(local) < 3:
            local += EmailGenerator._random_tag(3 - len(local))
        if len(local) > 64:
            local = local[:64]

        # 合法性兜底
        if not EmailGenerator.VALID_LOCAL.match(local):
            local = EmailGenerator._sanitize_base(local)
            if not EmailGenerator.VALID_LOCAL.match(local):
                local = EmailGenerator._random_local(opt.min_len, min(opt.max_len, 16))

        return local

    # 示例：批量生成
    @staticmethod
    def generate_batch(count: int, opt: Options) -> List[str]:
        return [EmailGenerator.generate(opt) for _ in range(count)]


class Gender(Enum):
    MALE = "MALE"
    FEMALE = "FEMALE"
    RANDOM = "RANDOM"


class NameGenerator:
    """日本姓名生成工具。
    支持：
    - 随机姓氏/名字
    - 按性别区分（可选）
    - 输出日文或罗马拼音
    """

    LAST_NAMES = [
        "山田", "佐藤", "鈴木", "田中", "中村", "高橋", "伊藤", "渡辺", "小林", "加藤"
    ]

    FIRST_NAMES_MALE = [
        "太郎", "健", "翔", "大輔", "浩", "雄大", "雅人", "誠", "直樹", "健一"
    ]

    FIRST_NAMES_FEMALE = [
        "花子", "優子", "美咲", "彩香", "直子", "愛", "舞", "沙織", "真由美", "美穂"
    ]

    @staticmethod
    def random_last_name() -> str:
        """随机生成姓"""
        return random.choice(NameGenerator.LAST_NAMES)

    @staticmethod
    def random_first_name() -> str:
        """随机生成名（性别随机）"""
        return random.choice(random.choice([NameGenerator.FIRST_NAMES_MALE, NameGenerator.FIRST_NAMES_FEMALE]))

    @staticmethod
    def random_first_name_by_gender(gender: Gender) -> str:
        """按性别生成名"""
        if gender == Gender.MALE:
            return random.choice(NameGenerator.FIRST_NAMES_MALE)
        elif gender == Gender.FEMALE:
            return random.choice(NameGenerator.FIRST_NAMES_FEMALE)
        else:  # RANDOM
            return NameGenerator.random_first_name()

    @staticmethod
    def generate_full_name() -> str:
        """生成全名（日文）"""
        return f"{NameGenerator.random_last_name()} {NameGenerator.random_first_name()}"

    @staticmethod
    def generate_full_name_by_gender(gender: Gender) -> str:
        """生成全名（日文，可指定性别）"""
        return f"{NameGenerator.random_last_name()} {NameGenerator.random_first_name_by_gender(gender)}"

    @staticmethod
    def generate_names(n: int, gender: Gender) -> List[str]:
        """批量生成 n 个名字"""
        return [NameGenerator.generate_full_name_by_gender(gender) for _ in range(n)]

    @staticmethod
    def generate_romanized_name(gender: Gender) -> str:
        """随机生成罗马拼音形式（仅示例性映射）"""
        last_names = ["Yamada", "Sato", "Suzuki", "Tanaka", "Nakamura", "Takahashi"]
        last = random.choice(last_names)

        if gender == Gender.MALE:
            first_names = ["Taro", "Ken", "Sho", "Daisuke", "Hiroshi"]
        else:
            first_names = ["Hanako", "Yuko", "Ayaka", "Naoko", "Mai"]

        first = random.choice(first_names)
        return f"{last} {first}"

    @staticmethod
    def to_ascii_upper_name(input_str: str) -> str:
        """转换为ASCII大写名称"""
        if input_str is None:
            return ""

        # 在Python中，NFKC规范化
        import unicodedata
        s = unicodedata.normalize('NFKC', input_str).upper()

        # 只保留A-Z和空格
        s = ''.join(c if c.isalpha() or c == ' ' else ' ' for c in s)

        # 空格归一化
        s = ' '.join(s.split())
        return s

def generate_random_string(length=8):
    """生成指定长度的随机字符串（包含大小写字母和数字）"""
    # 定义字符集：大小写字母和数字
    characters = string.ascii_letters + string.digits
    # 从字符集中随机选择指定数量的字符并组合成字符串
    random_string = ''.join(random.choice(characters) for _ in range(length))
    return random_string

def normalize_month(m):
    """月份转换与校验：1/01/9/09 → 01..12"""
    if not m or not m.isdigit():
        raise ValueError("月份必须为数字")

    mi = int(m)
    if mi < 1 or mi > 12:
        raise ValueError("月份不在 1-12 之间")

    return f"{mi:02d}"


def normalize_year(y):
    """年份转换：四位原样；两位统一按 20xx 解释（如 29→2029, 39→2039）"""
    if not y or not y.isdigit():
        raise ValueError("年份必须为数字")

    if len(y) == 4:
        yi = int(y)
        if yi < 2000:
            raise ValueError("四位年份不应小于 2000")
        return y
    elif len(y) == 2:
        return "20" + y
    else:
        raise ValueError("年份长度应为2位或4位")

def parse_card_line(line):
    parts = line.split("|")
    if len(parts) != 3:
        raise ValueError("需要3个字段：卡号|月份|年份")

    card_number = parts[0].strip()
    month_raw = parts[1].strip()
    year_raw = parts[2].strip()

    if not card_number:
        raise ValueError("卡号为空")
    if not card_number.isdigit():
        raise ValueError("卡号必须为纯数字")

    month = normalize_month(month_raw)  # 01..12
    year = normalize_year(year_raw)  # 2028 或 2029 等
    return card_number, month, year


class CardNameFormatter:
    @staticmethod
    def jp_to_ascii_upper(jp_name):
        """把日文（汉字/假名）转罗马字，再转「半角英文・大写」"""
        if jp_name is None:
            return ""

        # 1) 统一到半角
        s = unicodedata.normalize('NFKC', jp_name)

        # 2) 日文 -> 拉丁（罗马字）
        try:
            # 使用pykakasi进行更准确的日文转罗马字
            kks = pykakasi.kakasi()
            result = kks.convert(s)
            s = " ".join([item['hepburn'] for item in result])
        except:
            # 备用方案：使用romkan（可能不如pykakasi准确）
            s = generate_random_string(length=6)

        # 3) 只保留字母和空格，并大写
        s = s.upper()
        s = re.sub(r'[^A-Z ]', ' ', s)  # 非字母和空格替换为空格
        s = re.sub(r'\s+', ' ', s).strip()

        return s

    @staticmethod
    def to_card_holder_first_last(last_jp, first_jp):
        """组装 FIRST LAST 顺序（常见要求）"""
        first_ascii = CardNameFormatter.jp_to_ascii_upper(first_jp)
        last_ascii = CardNameFormatter.jp_to_ascii_upper(last_jp)
        return f"{first_ascii} {last_ascii}".strip()

    @staticmethod
    def to_card_holder_last_first(last_jp, first_jp):
        """组装 LAST FIRST 顺序（备用）"""
        first_ascii = CardNameFormatter.jp_to_ascii_upper(first_jp)
        last_ascii = CardNameFormatter.jp_to_ascii_upper(last_jp)
        return f"{last_ascii} {first_ascii}".strip()




if __name__ == '__main__':
    name = NameGenerator.generate_full_name()
    jp_name = CardNameFormatter.jp_to_ascii_upper(name)
    print(jp_name)
